﻿using DevExpress.Data.Filtering;
using DevExpress.Data.Filtering.Helpers;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Model.Core;
using DevExpress.ExpressApp.Utils;
using DevExpress.Persistent.Validation;
using DevExpress.Spreadsheet;
using System.Collections.ObjectModel;
using System.Drawing;

namespace EasyXaf.ExcelImporters;

public class ExcelImporter
{
    private readonly ListView _listView;
    private readonly XafApplication _application;
    private readonly IModelImporting _importingModel;

    private readonly Dictionary<string, IList<string>> _booleanValueCache = new();
    private readonly Dictionary<string, IDictionary<string, object>> _enumValueCache = new();

    public event EventHandler<ProgressEventArgs> ProgressChanged;

    public ExcelImporter(ListView listView, XafApplication application, IModelImporting importingModel)
    {
        _listView = listView;
        _application = application;
        _importingModel = importingModel;
    }

    protected void OnProgressChanged(ProgressEventArgs args)
    {
        ProgressChanged?.Invoke(this, args);
    }

    private static IDictionary<int, IModelImportingMember> GetImportingMemberModelColumns(CellRange region, IModelImporting importingModel)
    {
        var startRowOffset = importingModel.StartRow - region.TopRowIndex;
        var startColumnOffset = importingModel.StartColumn - region.LeftColumnIndex;
        var importingMemberModelColumns = new Dictionary<int, IModelImportingMember>();
        for (var i = startColumnOffset; i < region.ColumnCount; i++)
        {
            var importingMemberModel = importingModel.Members
                .Where(m => m.AllowUpdateMember)
                .FirstOrDefault(m => m.GetCaption() == region[startRowOffset, i].DisplayText);
            if (importingMemberModel != null)
            {
                importingMemberModelColumns[i] = importingMemberModel;
            }
        }
        return importingMemberModelColumns;
    }

    private bool? GetBooleanValue(IModelImportingMember importingMemberModel, string valueText)
    {
        var key = importingMemberModel.Property.Name;
        if (!_booleanValueCache.ContainsKey(key))
        {
            if (!string.IsNullOrWhiteSpace(importingMemberModel.PredefinedValues))
            {
                _booleanValueCache[key] = importingMemberModel.PredefinedValues.Split(',').ToList();
            }
            else
            {
                _booleanValueCache[key] = importingMemberModel.GetBooleanValueTexts().ToList();
            }
        }

        return _booleanValueCache[key].IndexOf(valueText) switch
        {
            0 => false,
            1 => true,
            _ => null
        };
    }

    private object GetEnumValue(IModelImportingMember importingMemberModel, string valueText)
    {
        var key = importingMemberModel.Property.Name;
        if (!_enumValueCache.ContainsKey(key))
        {
            var enumType = importingMemberModel.GetMemberRealType();
            var enumDescriptor = new EnumDescriptor(enumType);
            var enumValues = enumDescriptor.Values;
            var enumDic = new Dictionary<string, object>();

            IList<string> enumValueTexts;
            if (!string.IsNullOrWhiteSpace(importingMemberModel.PredefinedValues))
            {
                enumValueTexts = importingMemberModel.PredefinedValues.Split(',').ToList();
            }
            else
            {
                enumValueTexts = enumType.GetEnumValueTexts().ToList();
            }

            for (var i = 0; i < enumValues.Length; i++)
            {
                var value = enumValues.GetValue(i);
                enumDic[Convert.ChangeType(value, typeof(int)).ToString()] = value;
                if (enumValueTexts.Count > i)
                {
                    enumDic[enumValueTexts[i]] = value;
                }
            }

            _enumValueCache[key] = enumDic;
        }

        if (_enumValueCache[key].TryGetValue(valueText, out var enumValue))
        {
            return enumValue;
        }

        return null;
    }

    private object GetPersistentObjectValue(IObjectSpace objectSpace, IModelImportingMember importingMemberModel, string criteriaText, object value, out string error)
    {
        error = null;

        if (string.IsNullOrWhiteSpace(criteriaText))
        {
            criteriaText = $"[{importingMemberModel.Property.MemberInfo.MemberTypeInfo.DefaultMember.Name}] = ?";
        }

        var criteria = CriteriaOperator.Parse(criteriaText, value);
        var memberType = importingMemberModel.GetMemberRealType();
        var objects = objectSpace.GetObjects(memberType, criteria);
        if (objects.Count > 1)
        {
            error = $"值'{value}'存在多个对应的记录";
        }
        else if (objects.Count == 1)
        {
            return objects[0];
        }

        return null;
    }

    private object GetDefaultValue(IObjectSpace objectSpace, object businessObject, IModelImportingMember importingMemberModel, out string error)
    {
        error = null;

        var defaultValueCriteria = importingMemberModel.DefaultValueCriteria;
        var memberType = importingMemberModel.GetMemberRealType();

        if (importingMemberModel.IsReferenceType())
        {
            var persistentObjectValue = GetPersistentObjectValue(objectSpace, importingMemberModel, defaultValueCriteria, null, out string e);
            if (e != null)
            {
                error = e;
            }
            else if (persistentObjectValue != null)
            {
                return persistentObjectValue;
            }
            else
            {
                error = $"在获取{importingMemberModel.GetCaption()}默认值时，没有找到值为'{defaultValueCriteria}'所对应的记录";
            }
        }
        else
        {
            var typeInfo = importingMemberModel.Importing.ModelClass.TypeInfo;
            var criteria = CriteriaOperator.Parse(defaultValueCriteria);
            var propertyDescriptorCollection = new XafCustomTypeDescriptor(typeInfo).GetProperties();
            var defaultValue = new ExpressionEvaluator(propertyDescriptorCollection, criteria).Evaluate(businessObject)?.ToString();

            if (memberType == typeof(string))
            {
                return defaultValue;
            }
            else if (string.IsNullOrWhiteSpace(defaultValue))
            {
                return null;
            }
            else if (memberType.IsNumericType())
            {
                return NumericHelper.GetNumericValue(defaultValue, memberType);
            }
            else if (memberType == typeof(Guid))
            {
                if (Guid.TryParse(defaultValue, out Guid value))
                {
                    return value;
                }
            }
            else if (memberType == typeof(DateTime))
            {
                if (DateTime.TryParse(defaultValue, out DateTime value))
                {
                    return value;
                }
            }
            else if (memberType == typeof(TimeSpan))
            {
                if (TimeSpan.TryParse(defaultValue, out TimeSpan value))
                {
                    return value;
                }
            }
            else if (memberType == typeof(DateTimeOffset))
            {
                if (DateTimeOffset.TryParse(defaultValue, out DateTimeOffset value))
                {
                    return value;
                }
            }
            else if (memberType == typeof(bool))
            {
                if (bool.TryParse(defaultValue, out bool value))
                {
                    return value;
                }
            }
            else if (memberType.IsEnum)
            {
                var enumValue = GetEnumValue(importingMemberModel, defaultValue);
                if (enumValue != null)
                {
                    return enumValue;
                }
                else
                {
                    error = $"在获取{importingMemberModel.GetCaption()}默认值时，值'{defaultValue}'不是{memberType.Name}中的值";
                }
            }
        }

        return null;
    }

    private object GetCellValue(IObjectSpace objectSpace, IModelImportingMember importingMemberModel, Cell cell, out List<string> errors)
    {
        errors = new List<string>();

        var cellValue = cell.Value;
        var memberType = importingMemberModel.GetMemberRealType();

        if (cellValue.IsError)
        {
            errors.Add($"{importingMemberModel.GetCaption()}数据不正确");
        }
        else if (importingMemberModel.IsImageType())
        {
            var picture = cell.Worksheet.Pictures.FirstOrDefault(p => p.TopLeftCell == cell);
            if (picture?.Image != null)
            {
                return picture.Image.GetImageBytes(picture.ImageFormat);
            }
            else if (importingMemberModel.IsRequired())
            {
                errors.Add($"{importingMemberModel.GetCaption()}为必须信息");
            }
        }
        else if (importingMemberModel.IsRequired() && !cell.HasData)
        {
            errors.Add($"{importingMemberModel.GetCaption()}为必须信息");
        }
        else if (!cell.HasData)
        {
            return null;
        }
        else if (memberType == typeof(string))
        {
            if (cellValue.IsText)
            {
                return cellValue.TextValue;
            }
            else
            {
                return cell.DisplayText;
            }
        }
        else if (string.IsNullOrWhiteSpace(cell.DisplayText))
        {
            return null;
        }
        else if (memberType.IsNumericType()
            || memberType == typeof(Guid)
            || memberType == typeof(DateTime)
            || memberType == typeof(TimeSpan)
            || memberType == typeof(DateTimeOffset))
        {
            var value = cell.GetCellValue(memberType, out var error);
            if (error != null)
            {
                errors.Add(error);
            }
            else
            {
                return value;
            }
        }
        else if (memberType == typeof(bool))
        {
            if (cellValue.IsBoolean)
            {
                return cellValue.BooleanValue;
            }
            else if (cellValue.IsText)
            {
                var booleanValue = GetBooleanValue(importingMemberModel, cellValue.TextValue);
                if (booleanValue.HasValue)
                {
                    return booleanValue.Value;
                }
                else
                {
                    errors.Add($"值'{cellValue.TextValue}'不是布尔类型中的值");
                }
            }
        }
        else if (memberType.IsEnum)
        {
            var enumValue = GetEnumValue(importingMemberModel, cellValue.TextValue);
            if (enumValue != null)
            {
                return enumValue;
            }
            else
            {
                errors.Add($"值'{cellValue.TextValue}'不是{memberType.Name}中的值");
            }
        }
        else if (importingMemberModel.IsReferenceType())
        {
            var criteria = importingMemberModel.ValueCriteria;
            var persistentObjectValue = GetPersistentObjectValue(objectSpace, importingMemberModel, criteria, cellValue.TextValue, out string error);
            if (error != null)
            {
                errors.Add(error);
            }
            else if (persistentObjectValue != null)
            {
                return persistentObjectValue;
            }
            else
            {
                errors.Add($"没有找到值为'{cellValue.TextValue}'所对应的记录");
            }
        }

        return null;
    }

    private bool ValidateBusinessObject(object businessObject, IObjectSpace objectSpace, IModelImporting importingModel, out IDictionary<string, List<string>> errors)
    {
        var internalErrors = errors = new Dictionary<string, List<string>>();

        void AddError(string propertyName, string errorMessage)
        {
            if (!internalErrors.ContainsKey(propertyName))
            {
                internalErrors[propertyName] = new List<string>();
            }
            internalErrors[propertyName].Add(errorMessage);
        }

        void ProcessValidationResult(RuleSetValidationResult result)
        {
            var resultItems = result.Results
                .Where(r => r.ValidationOutcome == ValidationOutcome.Error)
                .ToList();

            foreach (var resultItem in resultItems)
            {
                var errorMessage = resultItem.ErrorMessage;
                if (resultItem.Rule.UsedProperties.Any())
                {
                    // 如果包含多个被用到的属性，每一个属性都有一个错误信息
                    foreach (var usedProperty in resultItem.Rule.UsedProperties)
                    {
                        AddError(usedProperty, errorMessage);
                    }
                }
                else
                {
                    // 没有对应的属性，采用特殊标记，作为整个对象的错误信息
                    AddError("__OBJ_ERROR__", errorMessage);
                }
            }
        };

        var ruleSet = Validator.GetService(_application.ServiceProvider);

        // 验证最大值与最小值（模板中定义的）
        foreach (var importingMemberModel in importingModel.Members)
        {
            if (!string.IsNullOrWhiteSpace(importingMemberModel.MinimumValue) || !string.IsNullOrWhiteSpace(importingMemberModel.MaximumValue))
            {
                var property = importingMemberModel.Property;
                var ruleId = $"{((ModelNode)importingMemberModel).Id}_RuleRange";
                var ruleRange = new RuleRange(ruleId, property.MemberInfo, ContextIdentifier.Save);
                ruleRange.Properties.MinimumValue = importingMemberModel.MinimumValue;
                ruleRange.Properties.MaximumValue = importingMemberModel.MaximumValue;
                var rules = new ReadOnlyCollection<IRule>(new List<IRule> { ruleRange });
                var result = ruleSet.ValidateTarget(objectSpace, businessObject, rules, ContextIdentifier.Save);
                if (result.ValidationOutcome == ValidationOutcome.Error)
                {
                    ProcessValidationResult(result);
                }
            }
        }

        // 验证BO中定义的Rule
        var boValidationResult = ruleSet.ValidateTarget(objectSpace, businessObject, ContextIdentifier.Save);
        if (boValidationResult.ValidationOutcome == ValidationOutcome.Error)
        {
            ProcessValidationResult(boValidationResult);
        }

        // 验证Model中定义的Rule
        foreach (var validationRuleModel in importingModel.Validations)
        {
            var ruleCriteriaProperties = new RuleCriteriaProperties
            {
                Id = $"{((ModelNode)validationRuleModel).Id}_RuleCriteria",
                TargetType = validationRuleModel.TargetType,
                TargetCriteria = validationRuleModel.Criteria,
                InvertResult = validationRuleModel.InvertResult,
                UsedProperties = validationRuleModel.UsedProperties,
                CustomMessageTemplate = validationRuleModel.MessageTemplate
            };

            var rules = new ReadOnlyCollection<IRule>(new List<IRule>() { new RuleCriteria(ruleCriteriaProperties) });
            var result = ruleSet.ValidateTarget(objectSpace, businessObject, rules, ContextIdentifier.Save);
            if (result.ValidationOutcome == ValidationOutcome.Error)
            {
                ProcessValidationResult(result);
            }
        }

        return !internalErrors.Any();
    }

    private bool ValidateBusinessObject(object businessObject, IObjectSpace objectSpace, IDictionary<Cell, IModelImportingMember> importingMemberModelCellMap)
    {
        var importingModel = importingMemberModelCellMap.Values.First().Importing;
        if (!ValidateBusinessObject(businessObject, objectSpace, importingModel, out var errors))
        {
            var businessObjectErrors = new List<string>();
            foreach (var propertyName in errors.Keys)
            {
                // 没有对应的属性，整个对象的错误信息
                if (propertyName == "__OBJ_ERROR__")
                {
                    businessObjectErrors.AddRange(errors[propertyName]);
                }
                else
                {
                    var cell = importingMemberModelCellMap
                        .Where(x => x.Value.Property.Name == propertyName)
                        .Select(x => x.Key)
                        .FirstOrDefault();

                    if (cell != null)
                    {
                        AddCellErrorComment(cell, importingModel, string.Join("\r\n", errors[propertyName]));
                    }
                    else
                    {
                        businessObjectErrors.AddRange(errors[propertyName]);
                    }
                }
            }

            // 没有指定属性的验证错误，将错误信息放在第一列中
            if (businessObjectErrors.Any())
            {
                var cell = importingMemberModelCellMap.First().Key;
                AddCellErrorComment(cell, importingModel, string.Join("\r\n", businessObjectErrors));
            }
            return false;
        }
        return true;
    }

    private void AddCellErrorComment(Cell cell, IModelImporting importingModel, string errorMessage)
    {
        var comment = cell.Worksheet.Comments.GetComments(cell).FirstOrDefault();
        if (comment != null)
        {
            comment.Text += $"\r\n{errorMessage}";
        }
        else
        {
            cell.FillColor = importingModel.ErrorBackgroundColor ?? Color.FromArgb(0xFF, 0xE6, 0x99);
            cell.Worksheet.Comments.Add(cell, string.Empty, errorMessage);
        }
    }

    private bool AssignBusinessObjectByCell(IObjectSpace objectSpace, object businessObject, Cell cell, IModelImportingMember importingMemberModel)
    {
        var success = true;

        // 再次导入时，需要将之前的背景颜色及注释重置
        cell.FillColor = Color.Transparent;
        cell.Worksheet.Comments.Remove(cell);

        var value = GetCellValue(objectSpace, importingMemberModel, cell, out var errors);
        if (errors.Any())
        {
            // 包含错误时，设置背景颜色并将错误添加到注释
            AddCellErrorComment(cell, importingMemberModel.Importing, string.Join("\r\n", errors));
            success = false;
        }
        else
        {
            if (!string.IsNullOrWhiteSpace(importingMemberModel.ValueCriteria) && !importingMemberModel.IsReferenceType())
            {
                var typeInfo = importingMemberModel.Importing.ModelClass.TypeInfo;
                var criteria = CriteriaOperator.Parse(importingMemberModel.ValueCriteria, value);
                var propertyDescriptorCollection = new XafCustomTypeDescriptor(typeInfo).GetProperties();
                value = new ExpressionEvaluator(propertyDescriptorCollection, criteria).Evaluate(businessObject);
            }

            // value为null时，使用默认值
            if (!string.IsNullOrWhiteSpace(importingMemberModel.DefaultValueCriteria) && value == null)
            {
                value = GetDefaultValue(objectSpace, businessObject, importingMemberModel, out string error);
                // 当含有错误时，说明默认值的设置有问题，需要直接抛出异常
                if (!string.IsNullOrWhiteSpace(error))
                {
                    throw new Exception(error);
                }
            }

            if (value != null || !objectSpace.IsNewObject(businessObject))
            {
                importingMemberModel.Property.MemberInfo.SetValue(businessObject, value);
            }
        }

        return success;
    }

    private object GetBusinessObject(IObjectSpace objectSpace, Type objectType, IDictionary<Cell, IModelImportingMember> importingMemberModelCellMap, out string error)
    {
        error = null;

        object businessObject = null;
        var importingModel = importingMemberModelCellMap.Values.First().Importing;

        if (importingModel.ImportingMode != ImportingMode.OnlyCreation && importingModel.ImportingMode != ImportingMode.AllCreation)
        {
            var keyCell = importingModel.KeyMember == null ? null : importingMemberModelCellMap
                .Where(x => x.Value == importingModel.KeyMember)
                .Select(x => x.Key)
                .FirstOrDefault();

            if (keyCell != null && !string.IsNullOrWhiteSpace(keyCell.DisplayText))
            {
                var keyCriteria = importingModel.KeyCriteria;
                var keyMemberType = importingModel.KeyMember.GetMemberRealType();
                var keyValue = keyCell.GetCellValue(keyMemberType, out error);
                if (error != null)
                {
                    AddCellErrorComment(keyCell, importingModel, error);
                    return null;
                }

                if (!string.IsNullOrWhiteSpace(keyCriteria))
                {
                    businessObject = objectSpace.FindObject(objectType, CriteriaOperator.Parse(keyCriteria, keyValue));
                }
                else
                {
                    businessObject = objectSpace.GetObjectByKey(objectType, keyValue);
                }
            }
        }

        if (importingModel.ImportingMode != ImportingMode.OnlyUpdate)
        {
            businessObject ??= objectSpace.CreateObject(objectType);
        }

        return businessObject;
    }

    private bool CreateBusinessObject(IObjectSpace objectSpace, Type objectType, IDictionary<Cell, IModelImportingMember> importingMemberModelCellMap)
    {
        var success = true;

        var businessObject = GetBusinessObject(objectSpace, objectType, importingMemberModelCellMap, out var error);
        if (error != null)
        {
            return false;
        }

        if (businessObject != null)
        {
            var importingModel = importingMemberModelCellMap.Values.First().Importing;

            foreach (var mapItem in importingMemberModelCellMap.OrderBy(x => x.Value.Index))
            {
                success &= AssignBusinessObjectByCell(objectSpace, businessObject, mapItem.Key, mapItem.Value);
            }

            foreach (var hiddenImportingMemberModel in importingModel.Members.Where(m => !m.VisibleInTemplate))
            {
                if (!string.IsNullOrWhiteSpace(hiddenImportingMemberModel.DefaultValueCriteria))
                {
                    var defaultValue = GetDefaultValue(objectSpace, businessObject, hiddenImportingMemberModel, out error);
                    // 当含有错误时，说明默认值的设置有问题，需要直接抛出异常
                    if (error != null)
                    {
                        throw new Exception(error);
                    }
                    else
                    {
                        hiddenImportingMemberModel.Property.MemberInfo.SetValue(businessObject, defaultValue);
                    }
                }
            }

            return success & ValidateBusinessObject(businessObject, objectSpace, importingMemberModelCellMap);
        }

        return success;
    }

    public bool ImportExcel(Stream stream)
    {
        using var workbook = new Workbook();
        workbook.LoadDocument(stream);

        var worksheetName = _importingModel.GetCaption();
        var worksheet = workbook.Worksheets.FirstOrDefault(s => s.Name == worksheetName);
        if (worksheet == null)
        {
            throw new Exception($"没有在当前工作簿中找到名为'{worksheetName}'的工作表");
        }

        var startRowIndex = _importingModel.StartRow;
        var startColumnIndex = _importingModel.StartColumn;
        var region = worksheet.Cells[startRowIndex, startColumnIndex].CurrentRegion;

        var importingMemberModelColumnMap = GetImportingMemberModelColumns(region, _importingModel);
        if (!importingMemberModelColumnMap.Any())
        {
            throw new Exception($"在'{worksheetName}'工作表中没有找到适合的导入列");
        }

        var mustIncludeImportingMemberModels = _importingModel.Members
            .Where(x => x.VisibleInTemplate)
            .Where(x => x.IsRequired() || x.MustIncludeColumn)
            .ToList();

        // KeyMember是Excel工作表中必须要包含的列
        if (_importingModel.KeyMember != null)
        {
            if (!mustIncludeImportingMemberModels.Contains(_importingModel.KeyMember))
            {
                mustIncludeImportingMemberModels.Add(_importingModel.KeyMember);
            }
        }

        foreach (var importingMemberModel in mustIncludeImportingMemberModels)
        {
            if (!importingMemberModelColumnMap.Values.Contains(importingMemberModel))
            {
                throw new Exception($"在'{worksheetName}'工作表中列'{importingMemberModel.GetCaption()}'是必须的");
            }
        }

        var success = true;
        var objectType = _importingModel.ModelClassType;
        using var objectSpace = _application.CreateObjectSpace(objectType);

        // 列的索引为区域的相对索引
        for (var i = startRowIndex - region.TopRowIndex + 1; i < region.RowCount; i++)
        {
            var importingMemberModelCellMap = importingMemberModelColumnMap.Keys.ToDictionary
            (
                key => region[i, key],
                key => importingMemberModelColumnMap[key]
            );

            // 创建业务对象
            success &= CreateBusinessObject(objectSpace, objectType, importingMemberModelCellMap);

            OnProgressChanged(new ProgressEventArgs(i + 1, region.RowCount - 1));
        }

        if (!success)
        {
            stream.Seek(0, SeekOrigin.Begin);
            stream.SetLength(0);
            workbook.SaveDocument(stream, DocumentFormat.OpenXml);
        }

        if (success)
        {
            objectSpace.CommitChanges();
        }

        return success;
    }
}
